
using System;
using System.Collections.Generic;

namespace Xeralabs.Core
{
    /// <summary>
    /// Encapsula las estadisticas de funcionamiento de una clase XGDESK. Tambien contiene una referencia
    /// estatica a una instancia de estadisticas globales.
    /// </summary>
    public sealed class KernelStats
    {
        #region Miembros de Clase Estatica
        private static bool _created = false;
        private static KernelStats _globalStats = null;

        /// <summary>
        /// Devuelve las estadisticas estaticas globales de todos los Kernels.
        /// </summary>
        public static KernelStats GlobalStatistics
        {
            get
            {
                if (_globalStats == null)
                    _globalStats = new KernelStats();

                return _globalStats;
            }
        }
        #endregion

        #region Datos de Instancia
        private DateTime _creationTime, _lastResetTime;
        private int _updateEvtS, _interactEvtS, _updateEvtR, _interactEvtR, _nullEvt, _wrongEvt, _revokedEvt;
        private double _initElapsedTime, _initRecordTime;
        private double _waitElapsedTime, _waitRecordTime;    

        private Dictionary<KernelErrorCode, int> _managedErrorTypesCount;
        #endregion

        #region Propiedades
        /// <summary>
        /// Devuelve el numero de eventos de actualizacion emitidos.
        /// Los eventos de actualizacion son aquellos en los que el guid emisor y el guid receptor son identicos.
        /// </summary>
        public int UpdateEventsSended { get { return this._updateEvtS; } }
        /// <summary>
        /// Devuelve el numero de eventos de interaccion emitidos.
        /// Los eventos de interaccion son aquellos en los que el guid emisor y el guid receptor son diferentes.
        /// </summary>
        public int InteractionMessagesSended { get { return this._interactEvtS; } }
        /// <summary>
        /// Devuelve el numero total de eventos emitidos.
        /// </summary>
        public int TotalMessagesSended { get { return this._updateEvtS + this._interactEvtS; } }

        /// <summary>
        /// Devuelve el numero de eventos de actualizacion recibidos y procesados.
        /// Los eventos de actualizacion son aquellos en los que el guid emisor y el guid receptor son identicos.
        /// </summary>
        public int UpdateMessagesRecived { get { return this._updateEvtR; } }
        /// <summary>
        /// Devuelve el numero de eventos de interaccion recibidos y procesados.
        /// Los eventos de interaccion son aquellos en los que el guid emisor y el guid receptor son diferentes.
        /// </summary>
        public int InteractionMessagesRecived { get { return this._interactEvtR; } }
        /// <summary>
        /// Devuelve el numero total de eventos recibidos y procesados.
        /// </summary>
        public int TotalMessagesRecived { get { return this._updateEvtR + this._interactEvtR; } }

        /// <summary>
        /// Devuelve el numero total de eventos con valor nulo encontrados en el sistema.
        /// </summary>
        public int NullMessages { get { return this._nullEvt; } }
        /// <summary>
        /// Devuelve el numero total de eventos con un receptor no encontrado.
        /// </summary>
        public int RevokedMessages { get { return this._revokedEvt; } }
        /// <summary>
        /// Devuelve el numero total de eventos revocados, con emisor no existente.
        /// </summary>
        public int WrongMessages { get { return this._wrongEvt; } }
        /// <summary>
        /// Devuelve el numero de eventos de actualizacion enviados, recibidos y procesados. Si hay una diferencia
        /// entre ambos, el valor devuelto sera el menor.
        /// Los eventos de actualizacion son aquellos en los que el guid emisor y el guid receptor son identicos.
        /// </summary>
        public int UpdateMessages { get { return (this._updateEvtS <= this._updateEvtR) ? this._updateEvtS : this._updateEvtR; } }
        /// <summary>
        /// Devuelve el numero de eventos de interaccion enviados, recibidos y procesados. Si hay una diferencia
        /// entre ambos, el valor devuelto sera el menor.
        /// Los eventos de interaccion son aquellos en los que el guid emisor y el guid receptor son diferentes.
        /// </summary>
        public int InteractionMessages { get { return (this._interactEvtS <= this._interactEvtR) ? this._interactEvtS : this._interactEvtR; } }
        /// <summary>
        /// Devuelve el numero total de eventos, enviados, recibidos y procesados, asi como valores nulos.
        /// </summary>
        public int TotalMessages { get { return (((this._updateEvtS <= this._updateEvtR) ? this._updateEvtS : this._updateEvtR) + ((this._interactEvtS <= this._interactEvtR) ? this._interactEvtS : this._interactEvtR)) + this._nullEvt + this._wrongEvt + this._revokedEvt; } }

        /// <summary>
        /// Devuelve el tiempo exacto de la creacion de la instancia.
        /// </summary>
        public DateTime CreationTime { get { return this._creationTime; } }
        /// <summary>
        /// Devuelve el tiempo exacto del ultimo reseteo de la instancia.
        /// </summary>
        public DateTime LastResetTime { get { return this._lastResetTime; } }
        /// <summary>
        /// Devuelve el tiempo exacto del ultimo reseteo de la instancia.
        /// </summary>
        public double InitializationElapsedTimeMs { get { return this._initElapsedTime; } }
        #endregion

        /// <summary>
        /// Constructor.
        /// </summary>
        public KernelStats()
        {
            if (_globalStats == null && !_created)
            {
                _created = true;
                _globalStats = new KernelStats();
            }
            _creationTime = DateTime.Now;
            this.Reset(false);
        }

        #region Metodos Simples
        /// <summary>
        /// Devielve el numero de errores administrados de un valor concreto de la enumeracion <see cref="KernelErrorCode"/>.
        /// </summary>
        /// <param name="errorCode">Indica el codigo de excepcion del cual se desea obtener el numero de errores.</param>
        /// <returns>El numero de errores administrados de <see cref="errorCode"/></returns>
        public int NumManagedErrors(KernelErrorCode errorCode)
        {
            try
            {
                return this._managedErrorTypesCount[errorCode];
            }
            catch (KeyNotFoundException)
            {
                return 0;
            }
        }

        /// <summary>
        /// Resetea los valores de la instancia, sin modificar la instancia estatica de estadisticas globales.
        /// </summary>
        public void Reset()
        {
            this.Reset(false);
        }
        /// <summary>
        /// Resetea los valores de la instancia.
        /// </summary>
        /// <param name="modifyGlobals">Indica si debe modificarse la instancia estatica de estadisticas globales.</param>
        public void Reset(bool modifyGlobals)
        {
            if (_globalStats != null && modifyGlobals)
            {
                _globalStats._updateEvtS -= this._updateEvtS;
                _globalStats._updateEvtR -= this._updateEvtR;

                _globalStats._interactEvtS -= this._interactEvtS;
                _globalStats._interactEvtR -= this._interactEvtR;

                _globalStats._nullEvt -= this._nullEvt;
                _globalStats._revokedEvt -= this._revokedEvt;
                _globalStats._wrongEvt -= this._wrongEvt;

                if (this._managedErrorTypesCount != null)
                {
                    foreach (KernelErrorCode code in this._managedErrorTypesCount.Keys)
                    {
                        _globalStats._managedErrorTypesCount[code] -= this._managedErrorTypesCount[code];
                    }
                }
            }

            this._updateEvtS = 0;
            this._updateEvtR = 0;

            this._interactEvtS = 0;
            this._interactEvtR = 0;

            this._nullEvt = 0;
            this._revokedEvt = 0;
            this._wrongEvt = 0;

            if (this._managedErrorTypesCount != null)
                this._managedErrorTypesCount = null;

            this._managedErrorTypesCount = new Dictionary<KernelErrorCode, int>();

            _lastResetTime = DateTime.Now;

            _initElapsedTime = -1.0;    //NO SE MODIFICA EN LA ESTATICA
            _initRecordTime = -1.0;
        }

        /// <summary>
        /// Registra un evento emitido con exito por una entidad.
        /// </summary>
        /// <param name="senderGUID">El emisor del evento.</param>
        /// <param name="reciverGUID">El receptor del evento.</param>
        internal void EventSended(ulong senderGUID, ulong reciverGUID)
        {
            if (senderGUID == reciverGUID)
            {
                this._updateEvtS++;
                _globalStats._updateEvtS++;
            }
            else
            {
                this._interactEvtS++;
                _globalStats._interactEvtS++;
            }
        }

        /// <summary>
        /// Registra la deteccion de un valor nulo entre los eventos del sistema.
        /// </summary>
        internal void NullEventDetected()
        {
            this._nullEvt++;
            _globalStats._nullEvt++;
        }
        internal void EventRevoked()
        {
            this._revokedEvt++;
            _globalStats._revokedEvt++;
        }
        internal void WrongEventHandled()
        {
            this._wrongEvt++;
            _globalStats._wrongEvt++;
        }

        /// <summary>
        /// Registra un evento procesado con exito por una entidad.
        /// </summary>
        /// <param name="senderGUID">El emisor del evento.</param>
        /// <param name="reciverGUID">El receptor del evento.</param>
        internal void EventProcessed(ulong senderGUID, ulong reciverGUID)
        {
            if (senderGUID == reciverGUID)
            {
                this._updateEvtR++;
                _globalStats._updateEvtR++;
            }
            else
            {
                this._interactEvtR++;
                _globalStats._interactEvtR++;
            }
        }
        

        /// <summary>
        /// Registra un error administrado con exito por el sistema.
        /// </summary>
        /// <param name="errorCode">El tipo de excepcion producida.</param>
        internal void ManagedKernelError(KernelErrorCode errorCode)
        {
            if (!this._managedErrorTypesCount.ContainsKey(errorCode))
            {
                this._managedErrorTypesCount.Add(errorCode, 0);
                if (!_globalStats._managedErrorTypesCount.ContainsKey(errorCode))
                {
                    _globalStats._managedErrorTypesCount.Add(errorCode, 0);
                }
            }
            this._managedErrorTypesCount[errorCode]++;
            _globalStats._managedErrorTypesCount[errorCode]++;
        }
        #endregion

        /// <summary>
        /// Registra el ultimo tiempo de inicializacion, el tiempo de inicializacion mas alto para la instancia de
        /// estadisticas globales, y el record de velocidad de inicializacion.
        /// </summary>
        /// <param name="elapsedTime">El tiempo transcurrirdo durante el proceso de inicializacion.</param>
        internal void InitializationTime(double elapsedTime)
        {
            if (elapsedTime > 0.0)
            {
                this._initElapsedTime = elapsedTime;

                if (this._initElapsedTime > _globalStats._initElapsedTime)
                    _globalStats._initElapsedTime = this._initElapsedTime;

                if ((this._initRecordTime <= 0.0) || (this._initRecordTime > this._initElapsedTime))
                {
                    this._initRecordTime = this._initElapsedTime;
                    if ((_globalStats._initRecordTime <= 0.0) || (_globalStats._initRecordTime > this._initRecordTime))
                    {
                        _globalStats._initRecordTime = this._initRecordTime;
                    }
                }
            }
        }
        /// <summary>
        /// Registra el ultimo tiempo de espera, el tiempo de espera mas alto para la instancia de
        /// estadisticas globales, y el record de tiempo de espera.
        /// </summary>
        /// <param name="elapsedTime">El tiempo de espera transcurrirdo entre dos procesos.</param>
        internal void WaitTime(double elapsedTime)
        {
            if (elapsedTime > 0.0)
            {
                this._waitElapsedTime = elapsedTime;

                if (this._waitElapsedTime > _globalStats._waitElapsedTime)
                    _globalStats._waitElapsedTime = this._waitElapsedTime;

                if ((this._waitRecordTime <= 0.0) || (this._waitRecordTime > this._waitElapsedTime))
                {
                    this._waitRecordTime = this._waitElapsedTime;
                    if ((_globalStats._waitRecordTime <= 0.0) || (_globalStats._waitRecordTime > this._waitRecordTime))
                    {
                        _globalStats._waitRecordTime = this._waitRecordTime;
                    }
                }
            }
        }
    }
}
